package com.cloud.phone.utils;

import com.cloud.phone.task.MsgTaskManger;
import com.cloud.phone.tools.LogUtils;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.AbortMultipartUploadRequest;
import com.obs.services.model.CompleteMultipartUploadRequest;
import com.obs.services.model.CompleteMultipartUploadResult;
import com.obs.services.model.DownloadFileRequest;
import com.obs.services.model.DownloadFileResult;
import com.obs.services.model.GetObjectRequest;
import com.obs.services.model.HeaderResponse;
import com.obs.services.model.InitiateMultipartUploadRequest;
import com.obs.services.model.InitiateMultipartUploadResult;
import com.obs.services.model.ObsObject;
import com.obs.services.model.PartEtag;
import com.obs.services.model.PutObjectRequest;
import com.obs.services.model.PutObjectResult;
import com.obs.services.model.UploadFileRequest;
import com.obs.services.model.UploadPartRequest;
import com.obs.services.model.UploadPartResult;

import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author lipu7
 * 华为obs上传下载工具
 */

public class HuaweiObsUtils {

    public static final String HUA_WEI_OBS_ENDPOINT_URL_FORMAT ="https://obs.%s.myhuaweicloud.com";
    private static final ExecutorService executorService = Executors.newFixedThreadPool(20);

    /**
     * 文件分段大小
     */
    private static final long PART_SIZE = 20 * 1024 * 1024L;
    /**
     * 断点续传文件分段大小
     */
    private static final long CHECK_POINT_PART_SIZE = 10 * 1024 * 1024L;
    /**
     * 断点续传文件最大并发数
     */
    private static final int CHECK_POINT_MAX_THREAD_NUM = 5;

    /**
     * @param endPoint 地区
     * @param ak
     * @param sk
     * @return
     */
    public static ObsClient getHuaWeiObsClient(String endPoint, String ak, String sk) {
        if (StringUtils.isAnyBlank(endPoint, ak, sk)) {
            return null;
        }
        // ObsClient是线程安全的，可在并发场景下使用
        ObsClient obsClient = null;
        try {
            String url = String.format(HUA_WEI_OBS_ENDPOINT_URL_FORMAT, endPoint);
            // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；本示例以ak和sk保存在环境变量中为例，运行本示例前请先在本地环境中设置环境变量ACCESS_KEY_ID和SECRET_ACCESS_KEY_ID。
            // 您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/intl/zh-cn/usermanual-ca/ca_01_0003.html
            // 创建ObsClient实例
            obsClient = new ObsClient(ak, sk, url);
        } catch (ObsException e) {
            LogUtils.w(""+e.getMessage()+e.getCause());
        }
        return obsClient;
    }

    /**
     * 文件上传
     *
     * @param endPoint
     * @param ak
     * @param sk
     * @param request  文件信息
     * @return
     */
    public static PutObjectResult upload(String endPoint, String ak, String sk, PutObjectRequest request) {
        if (Objects.isNull(request)) {
            return null;
        }
        ObsClient huaWeiObsClient = getHuaWeiObsClient(endPoint, ak, sk);
        if (Objects.isNull(huaWeiObsClient)) {
            return null;
        }
        PutObjectResult result = huaWeiObsClient.putObject(request);
        try {
            huaWeiObsClient.close();
        } catch (IOException e) {
        }
        return result;
    }

    /**
     * 创建文件夹
     *
     * @param endPoint   地区
     * @param ak
     * @param sk
     * @param bucketName obs 桶
     * @param directory  文件夹名
     * @return
     */
    public static PutObjectResult createDirectory(String endPoint, String ak, String sk, String bucketName, String directory) {
        if (StringUtils.isAllEmpty(bucketName, directory)) {
            return null;
        }

        ObsClient huaWeiObsClient = getHuaWeiObsClient(endPoint, ak, sk);
        if (Objects.isNull(huaWeiObsClient)) {
            return null;
        }
        if (!directory.endsWith("/")) {
            directory = directory + "/";
        }
        PutObjectResult result = huaWeiObsClient.putObject(bucketName, directory, new ByteArrayInputStream(new byte[0]));

        try {
            huaWeiObsClient.close();
        } catch (IOException e) {
        }
        return result;
    }

    /**
     * 大文件分段上传
     *
     * @param endPoint
     * @param ak
     * @param sk
     * @param request
     * @return
     */
    public static String largeDocumentUpload(String endPoint, String ak, String sk, PutObjectRequest request) {
        if (Objects.isNull(request)) {
            return StringUtils.EMPTY;
        }
        ObsClient huaWeiObsClient = getHuaWeiObsClient(endPoint, ak, sk);
        if (Objects.isNull(huaWeiObsClient)) {
            return "";
        }
        String bucketName = request.getBucketName();
        String objectKey = request.getObjectKey();
        File file = request.getFile();
        if (Objects.isNull(file) || StringUtils.isAllEmpty(bucketName, objectKey)) {
            return StringUtils.EMPTY;
        }
        InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucketName, objectKey);
        InitiateMultipartUploadResult initiated = huaWeiObsClient.initiateMultipartUpload(initiateMultipartUploadRequest);
        String uploadId = initiated.getUploadId();
        try {
            largeFileUpdatePart(uploadId, bucketName, objectKey, huaWeiObsClient, file);
            huaWeiObsClient.close();
            return uploadId;
        } catch (ObsException e) {
        } catch (Exception e) {
        }
        return uploadId;
    }

    /**
     * 取消分段上传任务
     * <p>
     * 选择取消多段上传任务，取消多段上传任务之后无法再次使用该上传ID上传任何段。然后，OBS将释放被取消的多段上传任务中的每个段数据的所有存储。
     * 如果有多段上传已在进行中，即使您已执行中止操作，它们仍可以上传成功或失败。若要释放所有分段使用的所有存储，必须在完成所有多段上传后再取消多段上传任务。
     *
     * @param endPoint
     * @param ak
     * @param sk
     * @param request
     * @param uploadId
     * @return
     */
    public static boolean cancelLargeDocumentUpload(String endPoint, String ak, String sk, PutObjectRequest request, String uploadId) {
        if (Objects.isNull(request)) {
            return true;
        }
        ObsClient huaWeiObsClient = getHuaWeiObsClient(endPoint, ak, sk);
        if (Objects.isNull(huaWeiObsClient)) {
            return true;
        }
        String bucketName = request.getBucketName();
        String objectKey = request.getObjectKey();
        if (StringUtils.isAllEmpty(bucketName, objectKey, uploadId)) {
            return true;
        }
        // 取消分段上传任务请求
        AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucketName, objectKey, uploadId);
        // 取消分段上传任务接口
        HeaderResponse response = huaWeiObsClient.abortMultipartUpload(abortMultipartUploadRequest);
        try {
            huaWeiObsClient.close();
        } catch (IOException e) {
        }
        return response.getStatusCode() < 400;
    }


    public static String checkPointFileUpload(String endPoint, String ak, String sk, PutObjectRequest request) {
        if (Objects.isNull(request)) {
            return StringUtils.EMPTY;
        }
        ObsClient huaWeiObsClient = getHuaWeiObsClient(endPoint, ak, sk);
        if (Objects.isNull(huaWeiObsClient)) {
            return StringUtils.EMPTY;
        }
        String bucketName = request.getBucketName();
        String objectKey = request.getObjectKey();
        if (StringUtils.isAllEmpty(bucketName, objectKey) || Objects.isNull(request.getFile())) {
            return StringUtils.EMPTY;
        }
        try {
            UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, objectKey);
            // 设置待上传的本地文件
            uploadFileRequest.setUploadFile(request.getFile().getAbsolutePath());
            // 设置分段上传时的最大并发数
            uploadFileRequest.setTaskNum(CHECK_POINT_MAX_THREAD_NUM);
            // 设置分段大小为10MB
            uploadFileRequest.setPartSize(CHECK_POINT_PART_SIZE);
            // 开启断点续传模式
            uploadFileRequest.setEnableCheckpoint(true);
            // 进行断点续传上传
            CompleteMultipartUploadResult result = huaWeiObsClient.uploadFile(uploadFileRequest);
            return result.getRequestId();
        } catch (ObsException e) {
        }
        return StringUtils.EMPTY;
    }

    /**
     * 下载文件
     *
     * @param obsClient
     * @param request
     * @return
     */
    public static InputStream downloadFile(ObsClient obsClient, GetObjectRequest request) {
        try {
            // 流式下载
            ObsObject obsObject = obsClient.getObject(request);
            // 读取对象内容
            return obsObject.getObjectContent();
        } catch (ObsException e) {
            // 发生异常时可再次调用断点续传上传接口进行重新上传
            // 请求失败,打印http状态码
            // 请求失败,打印服务端错误码
            // 请求失败,打印详细错误信息
        } catch (Exception e) {
        }
        return null;
    }


    /**
     * 断点续传下载
     *
     * @param obsClient
     * @param request
     * @param localfile 本地文件全路径 例如~/xxx.txt
     * @return
     */
    public static DownloadFileResult checkPointDownloadFile(ObsClient obsClient, DownloadFileRequest request, String localfile) {

        try {
            // 设置下载对象的本地文件路径
            request.setDownloadFile(localfile);
            // 设置分段下载时的最大并发数
            request.setTaskNum(CHECK_POINT_MAX_THREAD_NUM);
            // 设置分段大小为10MB
            request.setPartSize(CHECK_POINT_PART_SIZE);
            // 开启断点续传模式
            request.setEnableCheckpoint(true);
            // 进行断点续传下载
            return obsClient.downloadFile(request);
        } catch (ObsException e) {
            // 发生异常时可再次调用断点续传上传接口进行重新上传
        } catch (Exception e) {
            throw e;
        }
        return null;
    }

    private static void largeFileUpdatePart(String uploadId, String bucketName, String objectKey, ObsClient huaWeiObsClient, File file) {
        long largeFileSize = file.length();
        // 计算需要上传的段数
        long partCount = largeFileSize % PART_SIZE == 0 ? largeFileSize / PART_SIZE : largeFileSize / PART_SIZE + 1;
        final List<PartEtag> partEtags = Collections.synchronizedList(new ArrayList<PartEtag>());
        // 执行并发上传段
        for (int i = 0; i < partCount; i++) {
            // 分段在文件中的起始位置
            final long offset = i * PART_SIZE;
            // 分段大小
            final long currPartSize = (i + 1 == partCount) ? largeFileSize - offset : PART_SIZE;
            // 分段号
            final int partNumber = i + 1;
            executorService.execute(
                    () -> {
                        UploadPartRequest uploadPartRequest = new UploadPartRequest();
                        uploadPartRequest.setBucketName(bucketName);
                        uploadPartRequest.setObjectKey(objectKey);
                        uploadPartRequest.setUploadId(uploadId);
                        uploadPartRequest.setFile(file);
                        uploadPartRequest.setPartSize(currPartSize);
                        uploadPartRequest.setOffset(offset);
                        uploadPartRequest.setPartNumber(partNumber);
                        UploadPartResult uploadPartResult;
                        try {
                            uploadPartResult = huaWeiObsClient.uploadPart(uploadPartRequest);
                            partEtags.add(
                                    new PartEtag(uploadPartResult.getEtag(), uploadPartResult.getPartNumber()));
                        } catch (ObsException e) {
                            e.printStackTrace();
                        }
                    });
        }
        // 等待上传完成
        executorService.shutdown();
        while (!executorService.isTerminated()) {
            try {
                executorService.awaitTermination(5, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
            }
        }
        // 合并段
        CompleteMultipartUploadRequest completeMultipartUploadRequest =
                new CompleteMultipartUploadRequest(bucketName, objectKey, uploadId, partEtags);
        huaWeiObsClient.completeMultipartUpload(completeMultipartUploadRequest);
    }


    public static String uploadFile(File file) {
//        File file = new File("D:\\credentials\\WeChat_20240412160500.mp4");
        PutObjectRequest request = new PutObjectRequest();
        request.setBucketName("cloud-phone-huanduo-static");
        request.setObjectKey(file.getName());
        request.setFile(file);
        PutObjectResult result = upload("ap-southeast-3", "1RIKMLTPFZO8PPYBN0SM", "IGdpf8eaG4dcwZR4dHaA63LCZYXb2SiOLmoKNNld", request);
        String url=result.getObjectUrl();
        return url;
    }
}
